<?php

namespace App\Hydrators;

use App\Exceptions\API\ValidationFailed;
use App\Models\BaseModel;
use Incoming\Hydrator\HydratorInterface;
use Illuminate\Contracts\Validation\Factory as ValidatorFactory;

/**
 * Class Hydrator
 * @package App\Hydrators
 * @SuppressWarnings(PHPMD.NumberOfChildren)
 */
abstract class Hydrator implements HydratorInterface
{
    protected $validatorFactory;

    public function __construct()
    {
        $this->validatorFactory = app(ValidatorFactory::class);
    }

    /**
     * @param mixed $incoming
     * @param array $rules
     */
    public function validate($incoming, array $rules)
    {
        $payload = self::toArrayRecursive($incoming);
        $validator = $this->validatorFactory->make($payload, $rules);

        if ($validator->fails()) {
            throw new ValidationFailed('The data failed to pass validation', $validator->errors());
        }
    }

    /**
     * @param mixed $incoming
     * @param BaseModel $model
     * @return mixed
     */
    public function hydrate($incoming, $model)
    {
        if ($model->exists) {
            $this->validate($incoming, $this->getUpdateRules());

            return $this->hydrateForUpdate($incoming, $model);
        }
        $this->validate($incoming, $this->getCreateRules());

        return $this->hydrateForCreate($incoming, $model);
    }

    /**
     * @param mixed $incoming
     * @return array
     */
    public static function toArrayRecursive($incoming)
    {
        $result = [];
        $incoming = $incoming->toArray();
        foreach ($incoming as $key => $item) {
            $result[$key] = $item;
            if (is_object($item)) {
                $result[$key] = self::toArrayRecursive($item);
            }
        }

        return $result;
    }

    abstract protected function getCreateRules();

    abstract protected function getUpdateRules();

    abstract protected function hydrateForCreate($incoming, $model);

    abstract protected function hydrateForUpdate($incoming, $model);
}
